Get rid of the newline-that-could-not-be-deleted; buffers may now be
authorHavoc Pennington <hp@redhat.com>
Mon, 24 Sep 2001 23:12:39 +0000 (23:12 +0000)
committerHavoc Pennington <hp@src.gnome.org>
Mon, 24 Sep 2001 23:12:39 +0000 (23:12 +0000)
2001-09-24  Havoc Pennington  <hp@redhat.com>

* gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
gtktextlayout.c:
Get rid of the newline-that-could-not-be-deleted; buffers may
now be zero-length. Much easier to fix than expected, once
I figured out the right way to do it. However, there are
various subtle bugs introduced by this that will have to get
sorted out. Please use bugzilla.

15 files changed:
ChangeLog
ChangeLog.pre-2-0
ChangeLog.pre-2-10
ChangeLog.pre-2-2
ChangeLog.pre-2-4
ChangeLog.pre-2-6
ChangeLog.pre-2-8
docs/reference/gtk/text_widget.sgml
docs/reference/gtk/tmpl/gtkmenu.sgml
gtk/gtktextbtree.c
gtk/gtktextbtree.h
gtk/gtktextbuffer.c
gtk/gtktextiter.c
gtk/gtktextlayout.c
tests/testtextbuffer.c

index 2cd22ef4bf520ad5343ed727dfc48f530d1be9ec..38dc70b5b7b814f8f01b3c165b1cc237c0a37786 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2001-09-24  Havoc Pennington  <hp@redhat.com>
+
+       * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
+       gtktextlayout.c:
+       Get rid of the newline-that-could-not-be-deleted; buffers may 
+       now be zero-length. Much easier to fix than expected, once 
+       I figured out the right way to do it. However, there are 
+       various subtle bugs introduced by this that will have to get 
+       sorted out. Please use bugzilla.
+
 Mon Sep 24 15:09:08 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a
index 2cd22ef4bf520ad5343ed727dfc48f530d1be9ec..38dc70b5b7b814f8f01b3c165b1cc237c0a37786 100644 (file)
@@ -1,3 +1,13 @@
+2001-09-24  Havoc Pennington  <hp@redhat.com>
+
+       * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
+       gtktextlayout.c:
+       Get rid of the newline-that-could-not-be-deleted; buffers may 
+       now be zero-length. Much easier to fix than expected, once 
+       I figured out the right way to do it. However, there are 
+       various subtle bugs introduced by this that will have to get 
+       sorted out. Please use bugzilla.
+
 Mon Sep 24 15:09:08 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a
index 2cd22ef4bf520ad5343ed727dfc48f530d1be9ec..38dc70b5b7b814f8f01b3c165b1cc237c0a37786 100644 (file)
@@ -1,3 +1,13 @@
+2001-09-24  Havoc Pennington  <hp@redhat.com>
+
+       * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
+       gtktextlayout.c:
+       Get rid of the newline-that-could-not-be-deleted; buffers may 
+       now be zero-length. Much easier to fix than expected, once 
+       I figured out the right way to do it. However, there are 
+       various subtle bugs introduced by this that will have to get 
+       sorted out. Please use bugzilla.
+
 Mon Sep 24 15:09:08 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a
index 2cd22ef4bf520ad5343ed727dfc48f530d1be9ec..38dc70b5b7b814f8f01b3c165b1cc237c0a37786 100644 (file)
@@ -1,3 +1,13 @@
+2001-09-24  Havoc Pennington  <hp@redhat.com>
+
+       * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
+       gtktextlayout.c:
+       Get rid of the newline-that-could-not-be-deleted; buffers may 
+       now be zero-length. Much easier to fix than expected, once 
+       I figured out the right way to do it. However, there are 
+       various subtle bugs introduced by this that will have to get 
+       sorted out. Please use bugzilla.
+
 Mon Sep 24 15:09:08 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a
index 2cd22ef4bf520ad5343ed727dfc48f530d1be9ec..38dc70b5b7b814f8f01b3c165b1cc237c0a37786 100644 (file)
@@ -1,3 +1,13 @@
+2001-09-24  Havoc Pennington  <hp@redhat.com>
+
+       * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
+       gtktextlayout.c:
+       Get rid of the newline-that-could-not-be-deleted; buffers may 
+       now be zero-length. Much easier to fix than expected, once 
+       I figured out the right way to do it. However, there are 
+       various subtle bugs introduced by this that will have to get 
+       sorted out. Please use bugzilla.
+
 Mon Sep 24 15:09:08 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a
index 2cd22ef4bf520ad5343ed727dfc48f530d1be9ec..38dc70b5b7b814f8f01b3c165b1cc237c0a37786 100644 (file)
@@ -1,3 +1,13 @@
+2001-09-24  Havoc Pennington  <hp@redhat.com>
+
+       * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
+       gtktextlayout.c:
+       Get rid of the newline-that-could-not-be-deleted; buffers may 
+       now be zero-length. Much easier to fix than expected, once 
+       I figured out the right way to do it. However, there are 
+       various subtle bugs introduced by this that will have to get 
+       sorted out. Please use bugzilla.
+
 Mon Sep 24 15:09:08 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a
index 2cd22ef4bf520ad5343ed727dfc48f530d1be9ec..38dc70b5b7b814f8f01b3c165b1cc237c0a37786 100644 (file)
@@ -1,3 +1,13 @@
+2001-09-24  Havoc Pennington  <hp@redhat.com>
+
+       * gtk/gtktextiter.c, gtk/gtktextbuffer.c, gtk/gtktextbtree.c,
+       gtktextlayout.c:
+       Get rid of the newline-that-could-not-be-deleted; buffers may 
+       now be zero-length. Much easier to fix than expected, once 
+       I figured out the right way to do it. However, there are 
+       various subtle bugs introduced by this that will have to get 
+       sorted out. Please use bugzilla.
+
 Mon Sep 24 15:09:08 2001  Owen Taylor  <otaylor@redhat.com>
 
        * gtk/gtkwindow.c (gtk_window_move_resize): Don't wait for a
index 27a059a96d96d5c0f86b328b76d6f5d516c275cf..42d5525e2912518f647fff6f20f0f62ade8ba2d4 100644 (file)
@@ -102,6 +102,14 @@ between the old and new positions).
 </footnote>
 </para>
 
+<para>
+Text buffers always contain at least one line, but may be empty (that is,
+buffers can contain zero characters). The last line in the text buffer never
+ends in a line separator (such as newline); the other lines in the buffer always
+end in a line separator. Line separators count as characters when computing
+character counts and character offsets.
+</para>
+
 </refsect1>
 
 
index b8f6c879354f39e68fa2b852deeb6fbb9f7d7dc5..b51c366a4b8e9cfdb935d43b88694d40851c99fa 100644 (file)
@@ -96,9 +96,9 @@ Creates a new #GtkMenu.
 Adds a new #GtkMenuItem to the end of the menu's item list.
 </para>
 
+<!-- # Unused Parameters # -->
 @menu: a #GtkMenu.
 @child: The #GtkMenuItem to add.
-<!-- # Unused Parameters # -->
 @m: 
 @c: 
 
@@ -108,9 +108,9 @@ Adds a new #GtkMenuItem to the end of the menu's item list.
 Adds a new #GtkMenuItem to the beginning of the menu's item list.
 </para>
 
+<!-- # Unused Parameters # -->
 @menu: a #GtkMenu.
 @child: The #GtkMenuItem to add.
-<!-- # Unused Parameters # -->
 @menu_child: 
 @m: 
 @c: 
@@ -122,10 +122,10 @@ Adds a new #GtkMenuItem to the menu's item list at the position
 indicated by @position. 
 </para>
 
+<!-- # Unused Parameters # -->
 @menu: a #GtkMenu.
 @child: The #GtkMenuItem to add.
 @pos: 
-<!-- # Unused Parameters # -->
 @position: The position in the item list where @child is added.
 Positions are numbered from 0 to n-1.
 
index 810f3405f8d239a326ea0d5f3fd4bf7ce8349102..73cf60de37081582d225bc15e08542cd50ebdbf3 100644 (file)
@@ -185,22 +185,32 @@ struct _GtkTextBTree {
   guint tag_changed_handler;
   guint tag_removed_handler;
   /* Incremented when a segment with a byte size > 0
-     is added to or removed from the tree (i.e. the
-     length of a line may have changed, and lines may
-     have been added or removed). This invalidates
-     all outstanding iterators.
-  */
+   * is added to or removed from the tree (i.e. the
+   * length of a line may have changed, and lines may
+   * have been added or removed). This invalidates
+   * all outstanding iterators.
+   */
   guint chars_changed_stamp;
   /* Incremented when any segments are added or deleted;
-     this makes outstanding iterators recalculate their
-     pointed-to segment and segment offset.
-  */
+   * this makes outstanding iterators recalculate their
+   * pointed-to segment and segment offset.
+   */
   guint segments_changed_stamp;
 
-  GtkTextLine *end_iter_line;
+  /* Cache the last line in the buffer */
+  GtkTextLine *last_line;
+  guint last_line_stamp;
 
+  /* Cache the next-to-last line in the buffer,
+   * containing the end iterator
+   */
+  GtkTextLine *end_iter_line;
+  GtkTextLineSegment *end_iter_segment;
+  int end_iter_segment_byte_index;
+  int end_iter_segment_char_offset;
   guint end_iter_line_stamp;
-
+  guint end_iter_segment_stamp;
+  
   GHashTable *child_anchor_table;
 };
 
@@ -409,9 +419,15 @@ _gtk_text_btree_new (GtkTextTagTable *table,
   tree->chars_changed_stamp = g_random_int ();
   tree->segments_changed_stamp = g_random_int ();
 
+  tree->last_line_stamp = tree->chars_changed_stamp - 1;
+  tree->last_line = NULL;
+
   tree->end_iter_line_stamp = tree->chars_changed_stamp - 1;
+  tree->end_iter_segment_stamp = tree->segments_changed_stamp - 1;
   tree->end_iter_line = NULL;
-
+  tree->end_iter_segment_byte_index = 0;
+  tree->end_iter_segment_char_offset = 0;
+  
   g_object_ref (G_OBJECT (tree->table));
 
   tree->tag_changed_handler = g_signal_connect (G_OBJECT (tree->table),
@@ -570,6 +586,7 @@ _gtk_text_btree_delete (GtkTextIter *start,
     _gtk_text_btree_check (tree);
   
   {
+    /* FIXME this code should no longer be required */
     /*
      * The code below is ugly, but it's needed to make sure there
      * is always a dummy empty line at the end of the text.  If the
@@ -1903,11 +1920,20 @@ _gtk_text_btree_get_line (GtkTextBTree *tree,
   return line;
 }
 
+GtkTextLine*
+_gtk_text_btree_get_end_iter_line (GtkTextBTree *tree)
+{
+  return
+    _gtk_text_btree_get_line (tree,
+                              _gtk_text_btree_line_count (tree) - 1,
+                              NULL);
+}
+
 GtkTextLine*
 _gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
-                                 gint                char_index,
-                                 gint               *line_start_index,
-                                 gint               *real_char_index)
+                                  gint               char_index,
+                                  gint              *line_start_index,
+                                  gint              *real_char_index)
 {
   GtkTextBTreeNode *node;
   GtkTextLine *line;
@@ -1918,10 +1944,13 @@ _gtk_text_btree_get_line_at_char (GtkTextBTree      *tree,
 
   node = tree->root_node;
 
-  /* Clamp to valid indexes (-1 is magic for "highest index") */
-  if (char_index < 0 || char_index >= node->num_chars)
+  /* Clamp to valid indexes (-1 is magic for "highest index"),
+   * node->num_chars includes the two newlines that aren't really
+   * in the buffer.
+   */
+  if (char_index < 0 || char_index >= (node->num_chars - 1))
     {
-      char_index = node->num_chars - 1;
+      char_index = node->num_chars - 2;
     }
 
   *real_char_index = char_index;
@@ -2242,8 +2271,10 @@ _gtk_text_btree_line_count (GtkTextBTree *tree)
 gint
 _gtk_text_btree_char_count (GtkTextBTree *tree)
 {
-  /* Exclude newline in bogus last line */
-  return tree->root_node->num_chars - 1;
+  /* Exclude newline in bogus last line and the
+   * one in the last line that is after the end iterator
+   */
+  return tree->root_node->num_chars - 2;
 }
 
 #define LOTSA_TAGS 1000
@@ -3137,6 +3168,88 @@ _gtk_text_line_is_last (GtkTextLine *line,
   return line == get_last_line (tree);
 }
 
+static void
+ensure_end_iter_line (GtkTextBTree *tree)
+{
+  if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
+    {
+      int n_lines;
+      int real_line;
+
+      /* n_lines is without the magic line at the end */
+      n_lines = _gtk_text_btree_line_count (tree);
+      g_assert (n_lines >= 1);
+
+      tree->end_iter_line = _gtk_text_btree_get_line (tree, n_lines - 1, &real_line);
+      
+      tree->end_iter_line_stamp = tree->chars_changed_stamp;
+    }
+}
+
+static void
+ensure_end_iter_segment (GtkTextBTree *tree)
+{
+  if (tree->end_iter_segment_stamp != tree->segments_changed_stamp)
+    {
+      GtkTextLineSegment *seg;
+      GtkTextLineSegment *last_with_chars;
+
+      ensure_end_iter_line (tree);
+
+      last_with_chars = NULL;
+      
+      seg = tree->end_iter_line->segments;
+      while (seg != NULL)
+        {
+          if (seg->char_count > 0)
+            last_with_chars = seg;
+          seg = seg->next;
+        }
+
+      tree->end_iter_segment = last_with_chars;
+
+      /* We know the last char in the last line is '\n' */
+      tree->end_iter_segment_byte_index = last_with_chars->byte_count - 1;
+      tree->end_iter_segment_char_offset = last_with_chars->char_count - 1;
+      
+      tree->end_iter_segment_stamp = tree->segments_changed_stamp;
+    }
+}
+
+gboolean
+_gtk_text_line_contains_end_iter (GtkTextLine  *line,
+                                  GtkTextBTree *tree)
+{
+  ensure_end_iter_line (tree);
+
+  return line == tree->end_iter_line;
+}
+
+gboolean
+_gtk_text_btree_is_end (GtkTextBTree       *tree,
+                        GtkTextLine        *line,
+                        GtkTextLineSegment *seg,
+                        int                 byte_index,
+                        int                 char_offset)
+{
+  g_return_val_if_fail (byte_index >= 0 || char_offset >= 0, FALSE);
+  
+  /* Do this first to avoid walking segments in most cases */
+  if (!_gtk_text_line_contains_end_iter (line, tree))
+    return FALSE;
+
+  ensure_end_iter_segment (tree);
+
+  if (seg != tree->end_iter_segment)
+    return FALSE;
+
+  if (byte_index >= 0)
+    return byte_index == tree->end_iter_segment_byte_index;
+  else
+    return char_offset == tree->end_iter_segment_char_offset;
+}
+
 GtkTextLine*
 _gtk_text_line_next (GtkTextLine *line)
 {
@@ -3172,6 +3285,23 @@ _gtk_text_line_next (GtkTextLine *line)
     }
 }
 
+GtkTextLine*
+_gtk_text_line_next_excluding_last (GtkTextLine *line)
+{
+  GtkTextLine *next;
+  
+  next = _gtk_text_line_next (line);
+
+  /* If we were on the end iter line, we can't go to
+   * the last line
+   */
+  if (next && next->next == NULL && /* these checks are optimization only */
+      _gtk_text_line_next (next) == NULL)
+    return NULL;
+
+  return next;
+}
+
 GtkTextLine*
 _gtk_text_line_previous (GtkTextLine *line)
 {
@@ -3950,9 +4080,9 @@ node_compare (GtkTextBTreeNode *lhs,
 
 /* remember that tag == NULL means "any tag" */
 GtkTextLine*
-_gtk_text_line_next_could_contain_tag (GtkTextLine *line,
-                                      GtkTextBTree *tree,
-                                      GtkTextTag  *tag)
+_gtk_text_line_next_could_contain_tag (GtkTextLine  *line,
+                                       GtkTextBTree *tree,
+                                       GtkTextTag   *tag)
 {
   GtkTextBTreeNode *node;
   GtkTextTagInfo *info;
@@ -3968,17 +4098,17 @@ _gtk_text_line_next_could_contain_tag (GtkTextLine *line,
       /* Right now we can only offer linear-search if the user wants
        * to know about any tag toggle at all.
        */
-      return _gtk_text_line_next (line);
+      return _gtk_text_line_next_excluding_last (line);
     }
 
   /* Our tag summaries only have node precision, not line
-     precision. This means that if any line under a node could contain a
-     tag, then any of the others could also contain a tag.
-
-     In the future we could have some mechanism to keep track of how
-     many toggles we've found under a node so far, since we have a
-     count of toggles under the node. But for now I'm going with KISS.
-  */
+   * precision. This means that if any line under a node could contain a
+   * tag, then any of the others could also contain a tag.
+   * 
+   * In the future we could have some mechanism to keep track of how
+   * many toggles we've found under a node so far, since we have a
+   * count of toggles under the node. But for now I'm going with KISS.
+   */
 
   /* return same-node line, if any. */
   if (line->next)
@@ -4330,7 +4460,7 @@ summary_list_destroy (Summary *summary)
 static GtkTextLine*
 get_last_line (GtkTextBTree *tree)
 {
-  if (tree->end_iter_line_stamp != tree->chars_changed_stamp)
+  if (tree->last_line_stamp != tree->chars_changed_stamp)
     {
       gint n_lines;
       GtkTextLine *line;
@@ -4342,11 +4472,11 @@ get_last_line (GtkTextBTree *tree)
 
       line = _gtk_text_btree_get_line (tree, n_lines, &real_line);
 
-      tree->end_iter_line_stamp = tree->chars_changed_stamp;
-      tree->end_iter_line = line;
+      tree->last_line_stamp = tree->chars_changed_stamp;
+      tree->last_line = line;
     }
 
-  return tree->end_iter_line;
+  return tree->last_line;
 }
 
 /*
index 4e3b12236801bd636c839b20504a27ecaa7cd731..b3434ff4d6d0513bfa81786c2ffc58b8feb6b254 100644 (file)
@@ -23,6 +23,11 @@ guint _gtk_text_btree_get_chars_changed_stamp    (GtkTextBTree *tree);
 guint _gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree);
 void  _gtk_text_btree_segments_changed           (GtkTextBTree *tree);
 
+gboolean _gtk_text_btree_is_end (GtkTextBTree       *tree,
+                                 GtkTextLine        *line,
+                                 GtkTextLineSegment *seg,
+                                 int                 byte_index,
+                                 int                 char_offset);
 
 /* Indexable segment mutation */
 
@@ -82,6 +87,7 @@ void _gtk_text_btree_tag (const GtkTextIter *start,
 GtkTextLine * _gtk_text_btree_get_line          (GtkTextBTree      *tree,
                                                  gint               line_number,
                                                  gint              *real_line_number);
+GtkTextLine * _gtk_text_btree_get_end_iter_line (GtkTextBTree      *tree);
 GtkTextLine * _gtk_text_btree_get_line_at_char  (GtkTextBTree      *tree,
                                                  gint               char_index,
                                                  gint              *line_start_index,
@@ -205,9 +211,12 @@ gboolean            _gtk_text_line_byte_has_tag               (GtkTextLine
                                                                GtkTextBTree        *tree,
                                                                gint                 byte_in_line,
                                                                GtkTextTag          *tag);
-gboolean            _gtk_text_line_is_last                    (GtkTextLine  *line,
-                                                               GtkTextBTree *tree);
+gboolean            _gtk_text_line_is_last                    (GtkTextLine         *line,
+                                                               GtkTextBTree        *tree);
+gboolean            _gtk_text_line_contains_end_iter          (GtkTextLine         *line,
+                                                               GtkTextBTree        *tree);
 GtkTextLine *       _gtk_text_line_next                       (GtkTextLine         *line);
+GtkTextLine *       _gtk_text_line_next_excluding_last        (GtkTextLine         *line);
 GtkTextLine *       _gtk_text_line_previous                   (GtkTextLine         *line);
 void                _gtk_text_line_add_data                   (GtkTextLine         *line,
                                                                GtkTextLineData     *data);
index 48fe365726643ee45aed49ae029b024b550bde64..3a11c9dfa8dc3f44e5810fa73b53496a8e716f9f 100644 (file)
@@ -407,11 +407,8 @@ gtk_text_buffer_get_tag_table (GtkTextBuffer  *buffer)
  * @text: UTF-8 text to insert
  * @len: length of @text in bytes
  *
- * Deletes current contents of @buffer, and inserts @text instead.  If
- * @text doesn't end with a newline, a newline is added;
- * #GtkTextBuffer contents must always end with a newline. If @text
- * ends with a newline, the new buffer contents will be exactly
- * @text. If @len is -1, @text must be nul-terminated.
+ * Deletes current contents of @buffer, and inserts @text instead. If
+ * @len is -1, @text must be nul-terminated. @text must be valid UTF-8.
  **/
 void
 gtk_text_buffer_set_text (GtkTextBuffer *buffer,
@@ -426,12 +423,6 @@ gtk_text_buffer_set_text (GtkTextBuffer *buffer,
   if (len < 0)
     len = strlen (text);
 
-  /* Chop newline, since the buffer will already have one
-   * in it.
-   */
-  if (len > 0 && text[len-1] == '\n')
-    len -= 1;
-
   gtk_text_buffer_get_bounds (buffer, &start, &end);
 
   gtk_text_buffer_delete (buffer, &start, &end);
@@ -1135,14 +1126,6 @@ gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
 
   gtk_text_iter_order (start, end);
 
-  /* Somewhat annoyingly, if you try to delete the final newline
-   * the BTree will put it back; which means you can't deduce the
-   * final contents of the buffer purely by monitoring insert/delete
-   * signals on the buffer. But if you delete the final newline, any
-   * tags on the newline will go away, oddly. See comment in
-   * gtktextbtree.c. This is all sort of annoying, but really hard
-   * to fix.
-   */
   g_signal_emit (G_OBJECT (buffer),
                  signals[DELETE_RANGE],
                  0,
@@ -1163,11 +1146,6 @@ gtk_text_buffer_emit_delete (GtkTextBuffer *buffer,
  * calling this function; however, the @start and @end will be
  * re-initialized to point to the location where text was deleted.
  *
- * Note that the final newline in the buffer may not be deleted; a
- * #GtkTextBuffer always contains at least one newline.  You can
- * safely include the final newline in the range [@start,@end) but it
- * won't be affected by the deletion.
- *
  **/
 void
 gtk_text_buffer_delete (GtkTextBuffer *buffer,
@@ -1529,14 +1507,15 @@ gtk_text_buffer_mark_set (GtkTextBuffer     *buffer,
                           GtkTextMark       *mark)
 {
   /* IMO this should NOT work like insert_text and delete_range,
-     where the real action happens in the default handler.
-
-     The reason is that the default handler would be _required_,
-     i.e. the whole widget would start breaking and segfaulting
-     if the default handler didn't get run. So you can't really
-     override the default handler or stop the emission; that is,
-     this signal is purely for notification, and not to allow users
-     to modify the default behavior. */
+   * where the real action happens in the default handler.
+   * 
+   * The reason is that the default handler would be _required_,
+   * i.e. the whole widget would start breaking and segfaulting if the
+   * default handler didn't get run. So you can't really override the
+   * default handler or stop the emission; that is, this signal is
+   * purely for notification, and not to allow users to modify the
+   * default behavior.
+   */
 
   g_object_ref (G_OBJECT (mark));
 
@@ -2701,8 +2680,6 @@ clipboard_get_contents_cb (GtkClipboard     *clipboard,
       GtkTextIter start, end;
       
       gtk_text_buffer_get_bounds (contents, &start, &end);
-      /* strip off the trailing newline, it isn't part of the text that was cut */
-      gtk_text_iter_backward_char (&end);
       
       str = gtk_text_iter_get_visible_text (&start, &end);
       gtk_selection_data_set_text (selection_data, str);
@@ -3466,8 +3443,16 @@ _gtk_text_buffer_get_line_log_attrs (GtkTextBuffer     *buffer,
   
   g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
   g_return_val_if_fail (anywhere_in_line != NULL, NULL);
-  g_return_val_if_fail (!gtk_text_iter_is_end (anywhere_in_line), NULL);
 
+  /* special-case for empty last line in buffer */
+  if (gtk_text_iter_is_end (anywhere_in_line) &&
+      gtk_text_iter_get_line_offset (anywhere_in_line) == 0)
+    {
+      if (char_len)
+        *char_len = 0;
+      return NULL;
+    }
+  
   /* FIXME we also need to recompute log attrs if the language tag at
    * the start of a paragraph changes
    */
index ddbd273b4cf902b6a8c9d6c8650a64e2c3150b7b..6f7c778cdc0aca858e71e9f6f933a6a9b4582f2c 100644 (file)
@@ -1532,7 +1532,19 @@ gtk_text_iter_is_end (const GtkTextIter *iter)
 
   check_invariants (iter);
 
-  return _gtk_text_line_is_last (real->line, real->tree);
+  if (!_gtk_text_line_contains_end_iter (real->line, real->tree))
+    return FALSE;
+
+  /* Now we need the segments validated */
+  real = gtk_text_iter_make_real (iter);
+
+  if (real == NULL)
+    return FALSE;
+  
+  return _gtk_text_btree_is_end (real->tree, real->line,
+                                 real->segment,
+                                 real->segment_byte_offset,
+                                 real->segment_char_offset);
 }
 
 /**
@@ -1701,18 +1713,23 @@ gtk_text_iter_get_attributes (const GtkTextIter  *iter,
 /* The return value of this indicates WHETHER WE MOVED.
  * The return value of public functions indicates
  * (MOVEMENT OCCURRED && NEW ITER IS DEREFERENCEABLE)
+ *
+ * This function will not change the iterator if
+ * it's already on the last (end iter) line, i.e. it
+ * won't move to the end of the last line.
  */
 static gboolean
 forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
 {
-  GtkTextLine *new_line;
-
-  new_line = _gtk_text_line_next (real->line);
-
-  g_assert (new_line != real->line);
-
-  if (new_line != NULL)
+  if (!_gtk_text_line_contains_end_iter (real->line, real->tree))
     {
+      GtkTextLine *new_line;
+      
+      new_line = _gtk_text_line_next (real->line);
+      g_assert (new_line);
+      g_assert (new_line != real->line);
+      g_assert (!_gtk_text_line_is_last (new_line, real->tree));
+      
       real->line = new_line;
 
       real->line_byte_offset = 0;
@@ -1731,24 +1748,11 @@ forward_line_leaving_caches_unmodified (GtkTextRealIter *real)
     }
   else
     {
-      /* There is no way to move forward; we were already
-         at the "end" index. (the end index is the last
-         line pointer, segment_byte_offset of 0) */
-
-      g_assert (real->line_char_offset == 0 ||
-                real->line_byte_offset == 0);
-
-      /* The only indexable segment allowed on the bogus
-         line at the end is a single char segment containing
-         a newline. */
-      if (real->segments_changed_stamp ==
-          _gtk_text_btree_get_segments_changed_stamp (real->tree))
-        {
-          g_assert (real->segment->type == &gtk_text_char_type);
-          g_assert (real->segment->char_count == 1);
-        }
-      /* We leave real->line as-is */
-
+      /* There is no way to move forward a line; we were already at
+       * the line containing the end iterator.
+       * However we may not be at the end iterator itself.
+       */
+      
       return FALSE;
     }
 }
@@ -1950,6 +1954,8 @@ _gtk_text_iter_forward_indexable_segment (GtkTextIter *iter)
         {
           /* End of buffer */
 
+          g_assert (gtk_text_iter_is_end (iter));
+          
           check_invariants (iter);
 
           return FALSE;
@@ -2442,6 +2448,11 @@ gtk_text_iter_forward_line (GtkTextIter *iter)
     }
   else
     {
+      /* On the last line, move to end of it */
+      
+      if (!gtk_text_iter_is_end (iter))
+        gtk_text_iter_forward_to_end (iter);
+      
       check_invariants (iter);
       return FALSE;
     }
@@ -2789,7 +2800,9 @@ test_log_attrs (const GtkTextIter *iter,
 
   offset = gtk_text_iter_get_line_offset (iter);
 
-  g_assert (char_len > 0);
+  /* char_len may be 0 and attrs will be NULL if so, if
+   * iter is the end iter and the last line is empty
+   */
   
   if (offset < char_len)
     result = (* func) (attrs, offset, 0, char_len);
@@ -2814,8 +2827,10 @@ find_line_log_attrs (const GtkTextIter *iter,
                                                iter, &char_len);      
 
   offset = gtk_text_iter_get_line_offset (iter);
-  
-  g_assert (char_len > 0);
+
+  /* char_len may be 0 and attrs will be NULL if so, if
+   * iter is the end iter and the last line is empty
+   */
   
   if (offset < char_len)
     result = (* func) (attrs, offset, 0, char_len, found_offset,
@@ -3734,7 +3749,7 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
 
   current_line = real->line;
   next_line = _gtk_text_line_next_could_contain_tag (current_line,
-                                                    real->tree, tag);
+                                                     real->tree, tag);
 
   while (_gtk_text_iter_forward_indexable_segment (iter))
     {
@@ -3756,8 +3771,8 @@ gtk_text_iter_forward_to_tag_toggle (GtkTextIter *iter,
 
           current_line = real->line;
           next_line = _gtk_text_line_next_could_contain_tag (current_line,
-                                                            real->tree,
-                                                            tag);
+                                                             real->tree,
+                                                             tag);
         }
 
       if (gtk_text_iter_toggles_tag (iter, tag))
@@ -5175,5 +5190,8 @@ _gtk_text_iter_check (const GtkTextIter *iter)
             g_error ("wrong char index was cached");
         }
     }
+
+  if (_gtk_text_line_is_last (real->line, real->tree))
+    g_error ("Iterator was on last line (past the end iterator)");
 }
 
index 2b77c91b3c913762f5d3c0e33775445cac6e7862..bcb1aceaff98b52b750a62354ffc25077f5f1e21 100644 (file)
@@ -578,13 +578,11 @@ gtk_text_layout_get_lines (GtkTextLayout *layout,
   /* -1 since bottom_y is one past */
   last_btree_line =
     _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer),
-                                   layout, bottom_y - 1, NULL);
+                                    layout, bottom_y - 1, NULL);
 
   if (!last_btree_line)
     last_btree_line =
-      _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
-                               _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
-                               NULL);
+      _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer));
 
   g_assert (last_btree_line != NULL);
 
@@ -596,7 +594,7 @@ gtk_text_layout_get_lines (GtkTextLayout *layout,
       if (line == last_btree_line)
         break;
 
-      line = _gtk_text_line_next (line);
+      line = _gtk_text_line_next_excluding_last (line);
     }
 
   retval = g_slist_reverse (retval);
@@ -722,7 +720,7 @@ gtk_text_layout_real_invalidate (GtkTextLayout *layout,
       if (line == last_line)
         break;
 
-      line = _gtk_text_line_next (line);
+      line = _gtk_text_line_next_excluding_last (line);
     }
 
   gtk_text_layout_invalidated (layout);
@@ -862,7 +860,7 @@ gtk_text_layout_validate_yrange (GtkTextLayout *layout,
         }
 
       seen += line_data->height;
-      line = _gtk_text_line_next (line);
+      line = _gtk_text_line_next_excluding_last (line);
     }
 
   /* If we found and validated any invalid lines, update size and
@@ -2005,6 +2003,9 @@ line_display_index_to_iter (GtkTextLayout      *layout,
                            gint                index,
                            gint                trailing)
 {
+  g_return_if_fail (!_gtk_text_line_is_last (display->line,
+                                             _gtk_text_buffer_get_btree (layout->buffer)));
+  
   if (index >= display->insert_index + layout->preedit_len)
     index -= layout->preedit_len;
   else if (index > display->insert_index)
@@ -2066,8 +2067,8 @@ get_line_at_y (GtkTextLayout *layout,
                                          layout, y, line_top);
   if (*line == NULL)
     {
-      *line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
-                                       _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
+      *line = _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer));
+      
       if (line_top)
         *line_top =
           _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
@@ -2306,9 +2307,8 @@ find_display_line_below (GtkTextLayout *layout,
   if (!line)
     {
       line =
-        _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
-                                 _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1,
-                                 NULL);
+        _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer));
+
       line_top =
         _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer),
                                       line, layout);
@@ -2346,7 +2346,7 @@ find_display_line_below (GtkTextLayout *layout,
       line_top += display->bottom_margin;
       gtk_text_layout_free_line_display (layout, display);
 
-      next = _gtk_text_line_next (line);
+      next = _gtk_text_line_next_excluding_last (line);
       if (!next)
         found_line = line;
 
@@ -2374,8 +2374,8 @@ find_display_line_above (GtkTextLayout *layout,
   line = _gtk_text_btree_find_line_by_y (_gtk_text_buffer_get_btree (layout->buffer), layout, y, &line_top);
   if (!line)
     {
-      line = _gtk_text_btree_get_line (_gtk_text_buffer_get_btree (layout->buffer),
-                                      _gtk_text_btree_line_count (_gtk_text_buffer_get_btree (layout->buffer)) - 1, NULL);
+      line = _gtk_text_btree_get_end_iter_line (_gtk_text_buffer_get_btree (layout->buffer));
+      
       line_top = _gtk_text_btree_find_line_top (_gtk_text_buffer_get_btree (layout->buffer), line, layout);
     }
 
@@ -2658,7 +2658,7 @@ gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
       
       gtk_text_layout_free_line_display (layout, display);
 
-      line = _gtk_text_line_next (line);
+      line = _gtk_text_line_next_excluding_last (line);
     }
 
   return
@@ -2939,7 +2939,7 @@ gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
         }
       else if (new_index > byte_count)
         {
-          line = _gtk_text_line_next (line);
+          line = _gtk_text_line_next_excluding_last (line);
           if (!line)
             goto done;
 
index 4ac0dafefcdfa28c07371ce0cbf11b79d4e1ea0a..f58383e9ea3ee9c64003402ffbf4a60151f2fdda 100644 (file)
@@ -2,6 +2,7 @@
 
 
 #include <stdio.h>
+#include <string.h>
 
 #include <gtk/gtk.h>
 #include "../gtk/gtktexttypes.h" /* Private header, for UNKNOWN_CHAR */
@@ -28,7 +29,8 @@ main (int argc, char** argv)
   int n;
   gunichar ch;
   GtkTextIter start, end;
-
+  gchar *text;
+  
   gtk_init (&argc, &argv);
 
   /* Check UTF8 unknown char thing */
@@ -49,12 +51,34 @@ main (int argc, char** argv)
     g_error ("%d lines, expected 1", n);
 
   n = gtk_text_buffer_get_char_count (buffer);
-  if (n != 1)
-    g_error ("%d chars, expected 1", n);
+  if (n != 0)
+    g_error ("%d chars, expected 0", n);
 
   /* Run gruesome alien test suite on buffer */
   run_tests (buffer);
 
+  /* Check set/get text */
+  gtk_text_buffer_set_text (buffer, "Hello", -1);
+  if (gtk_text_buffer_get_char_count (buffer) != g_utf8_strlen ("Hello", -1))
+    g_error ("Wrong number of chars (%d not %d)",
+             gtk_text_buffer_get_char_count (buffer),
+             (int) g_utf8_strlen ("Hello", -1));
+  gtk_text_buffer_get_bounds (buffer, &start, &end);
+  text = gtk_text_buffer_get_text (buffer, &start, &end, TRUE);
+  if (strcmp (text, "Hello") != 0)
+    g_error ("Got '%s' as buffer contents", text);
+  g_free (text);
+
+  gtk_text_buffer_set_text (buffer, "", -1);
+
+  n = gtk_text_buffer_get_line_count (buffer);
+  if (n != 1)
+    g_error ("%d lines, expected 1", n);
+
+  n = gtk_text_buffer_get_char_count (buffer);
+  if (n != 0)
+    g_error ("%d chars, expected 0", n);
+  
   /* Put stuff in the buffer */
 
   fill_buffer (buffer);
@@ -73,8 +97,8 @@ main (int argc, char** argv)
     g_error ("%d lines, expected 1", n);
 
   n = gtk_text_buffer_get_char_count (buffer);
-  if (n != 1)
-    g_error ("%d chars, expected 1", n);
+  if (n != 0)
+    g_error ("%d chars, expected 0", n);
 
   run_tests (buffer);